Ana içeriğe geç

Iterators

Daha önceki derslerimizde iteratorleri görüntüledik. Ancak şimdi yeniden kısaca bir göz atabiliriz.

let vec = vec![1,2,3]
for val in vec.iter{
println!("{}", val);
}

Yukarıda daha önceden de gördüğümüz bir iteratör örneği verilmiştir. iter yapısı sayesinde belirli bir liste içerisindeki tüm elamanları bir değişkenlere eşitleyerek işlemler yapabiliriz.

Rust içerisinde bulunan tüm iteratorler Rust ile hazır gelen Iterator trait'ini kullanmaktadır. Bu yapı şu şekilde gözükmektedir.

pub trait Iterator {
type item;
fn next(&mut self) -> Option<Self::Item>;
}

Bu yapılar içerisinde birçok kavram ile gelmektedir. İçerisinde bulunan next yapısı bir sonraki gelecek verinin Some veya None olmasını sağlamaktadır.

Ayrıca into yapısına da sahip olmaktayız.

let vec2 = vec![1,2,3];
let mut iter = (&vec2).into_iter();

while let Some(v) = iter.next(){
println!("{}", v);
}

Yukarıda verilen yapıda da gördüğümüz gibi into_iter yapısını da kullanarak koleksiyonların içerisindeki değerlere erişim sağlayabiliriz.

NOT: Bir Iterator, Iterator Trait'ini içeren tüm yapılardan oluşmaktadır. Bir Iterable yapısı ise içerisine tüm into yapısını içeren yapılardır.

Hadi öğrendiklerimizi biraz daha açalım. Item adında bir struct yapısı oluşturalım

#[derive(Debug)]
struct Item{
name: String
}

Sonrasında bu itemleri kontrol edecek bir fonksiyon yazabiliriz.

fn check_inventory(items: Vec<Item>, product: String) -> Vec<Item>{
items.into_iter().filter(|i| i.name == product).collect()
}

Yapımızı oluşturduk. Artık içerisine veri eklemeye başlayabiliriz.

let mut vec: Vec<Item> = Vec::new();

vec.push(Item {
name: String::from("coat");
});
vec.push(Item {
name: String::from("shirt");
});
vec.push(Item {
name: String::from("shorts");
});
vec.push(Item {
name: String::from("shoes");
});

Sonrasında artık tanımladığımız fonksiyon ile envanteri kontrol edebiliz.

let cheched = fn check_inventory(vec, String::from("shirt"));

println!("{:?}",checked)

Yukarıda tanımladığımız checked_inventory yapımıza daha da ayrıntılı baktığımızda into_iter ile oluşturduğumuz bir iteratörün oluşturduğumuz vektörün sahipliğini aldığını sonrasında filter ile sadace içerisinde bulunan değerin true olduğu zamanlarda çalışmasını sağladığımız bir yapı vardır. İçerisinde bulunan closure ise true false değerinin oluşmasını sağlamaktadır. Oluşturduğumuz değerleri sonrasında colect ile toplarız.

Peki kendi iteratörlerimizi oluşturmak isteseydik bunu nasıl yapabilirdik? Hadi bu duruma yeni bir struct yapısı oluşturarak bakalım.

#[derive(Debug)]
struct Range{
start: u32,
end: u32
}

impl Iterator for Range {
type Item = u32,
fn next(&mut self) -> Option<self::Item> {
if self.start >= self.end {
return None;
}
let result = Some(self.start);
self.start += 1;
result
}
}

Sonrasında yapımızın çalışıp çalışmadığını test etmek için:

let mut range = Range{start: 0, end: 10};
for r in range {
println!("{}", r);
}

Kodumuzu çalıştırdığımızda 0'dan 10'a kadar başarılı bir şekilde yazdırıldığını görüntüleyebiliriz.

Daha önce de öğrendiğimiz üzere next fonksiyonunu eklediğimize göre bazı ekstra özellikler de almamız gerekmektedir. Bu durumun sağlandığını kontrol etmek için alt kısımda ifade edilen kodu yazabiliriz.

let vec: Vec<u32> = range.filter(|x| x %2 == 0).collect();

println!("{:?}", vec);